package com.itheima.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.*;
import com.itheima.service.DeptService;
import com.itheima.service.EmpExprService;
import com.itheima.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.core.Local;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class EmpServiceImpl extends ServiceImpl<EmpMapper, Emp> implements EmpService {
    /**
     * 条件分页查询
     * @param param  查询条件
     * @return  封装了数据库中的员工数据的总条数和条件查询得到的当前页的列表数据
     */

    @Autowired
    private DeptService deptService;
    @Autowired
    private EmpExprService empExprService;
    @Override
    public PageResult<Emp> getPageResult(EmpQueryParam param) {
        /*Page<Emp> page = Page.of(param.getPage(), param.getPageSize());// 获取传入的页码和每页条数封装到Page对象中
        // 根据最后修改时间倒序排序
        page.addOrder(OrderItem.desc("update_time"));
        // 调用page方法完成分页查询
        page = page(page);
        // 封装并返回，当前页面总条数 和 当前页的列表数据
        return new PageResult<>(page.getTotal(), page.getRecords());*/

        // 先判断条件，再进行分页

        /*// 1.构造条件查询的条件
        LambdaQueryWrapper<Emp> queryWrapper = new LambdaQueryWrapper<>();
        // 封装查询条件（where）
         StrUtil.isEmpty(param.getName());   // 给定值为null或""时为ture
         StrUtil.isBlank(param.getName());   // 给定值为null或""或"  "时为ture
        queryWrapper.like(StrUtil.isNotBlank(param.getName()),Emp::getName,param.getName())  // 模糊查询名字包含给定关键字的，并且判断当查询条件为null,""或者为"  "时，不查询
                .eq(ObjectUtil.isNotNull(param.getGender()),Emp::getGender,param.getGender())  // 查询性别为给定性别的,并且判断当查询条件为null时不查询
                .between(ObjectUtil.isNotNull(param.getBegin()) && ObjectUtil.isNotNull(param.getEnd()),Emp::getEntryDate,param.getBegin(),param.getEnd()); // 查询入职时间在给定时间之内的
        // 2.发起分页查询
        Page<Emp> page = Page.of(param.getPage(), param.getPageSize());
        page.addOrder(OrderItem.desc("update_time"));
        Page<Emp> pageResult = this.page(page, queryWrapper);  // 这两个参数分别为分页limit和条件where*/

        // 合并1，2步的简写方式（主流写法） 构建查询条件并发起分页查询
        Page<Emp> page = Page.of(param.getPage(), param.getPageSize());
        page.addOrder(OrderItem.desc("update_time"));
        Page<Emp> pageResult = this.lambdaQuery()
                .like(StrUtil.isNotBlank(param.getName()),Emp::getName,param.getName())  // 模糊查询名字包含给定关键字的，并且判断当查询条件为null,""或者为"  "时，不查询
                .eq(ObjectUtil.isNotNull(param.getGender()),Emp::getGender,param.getGender())  // 查询性别为给定性别的,并且判断当查询条件为null时不查询
                .between(ObjectUtil.isNotNull(param.getBegin()) && ObjectUtil.isNotNull(param.getEnd()),Emp::getEntryDate,param.getBegin(),param.getEnd())
                .page(page);
        // 获取符合查询条件的员工的所有信息列表，除了部门名称
        List<Emp> empList = pageResult.getRecords();

        /*// 遍历查询得到的每一个员工，拿到其部门Id，根据其部门Id查询其部门的所有信息封装到一个对象中，再由对象的方式获取部门的名称，最后将部门名称封装到员工对象中，以便在查询时，显示员工对应的部门名称
        empList.forEach(emp -> {
            Dept dept = deptService.getById(emp.getDeptId());
            String deptName = ObjectUtil.isEmpty(dept) || StrUtil.isBlank(dept.getName()) ? "X部门" : dept.getName();
            emp.setDeptName(deptName);
        }); // 但这种方式效率很低，有多少员工就要发起多少次部门查询，每次都在控制台输出，控制台信息非常臃肿*/
        // 3.处理部门名称（*重点理解）
        // 封装部门名称到员工信息中，以便查询出每个员工对应的部门名称
        // 拿到所有的部门Id并收集为set集合，意味着去掉了重复的部门Id，最终只得到6个部门Id
        Set<Integer> deptIds = empList.stream().map(Emp::getDeptId).collect(Collectors.toSet());
        // 根据这6个部门id，得到对应的6个部门信息，并封装到一个map集合中，key为部门Id，value为部门名称
        List<Dept> deptList = deptService.listByIds(deptIds);
        Map<Integer,String> detMap = deptList.stream().collect(Collectors.toMap(Dept::getId,Dept::getName));
        // 遍历员工列表，根据员工的部门id，在封装部门信息的map集合中获取到部门名称，并设置到员工对象中。如果员工的部门id在map集合中不存在，则默认设置为"X部门"。
        empList.forEach(emp -> emp.setDeptName(detMap.getOrDefault(emp.getDeptId(),"X部门")));

        // 4.封装分页结果集
        return new PageResult<>(pageResult.getTotal(),empList);
    }

    /**
     * 新增员工信息
     * @param emp 封装了前端传入的要新增的员工信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveEmp(Emp emp) {
        // 1.保存员工基本信息（主表），并补全基本属性
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        this.save(emp);
        // 2.保存员工的工作经历信息（从表）
        List<EmpExpr> exprList = emp.getExprList();
        if (!CollectionUtils.isEmpty(exprList)){
            // 给每一条工作经历添加对应的员工id，使工作经历表中的emp_id字段不为空，同时保证与员工表的关联
            exprList.forEach(expr -> expr.setEmpId(emp.getId()));
            empExprService.saveBatch(exprList);
        }
    }

    /**
     * 删除员工信息
     * @param ids 封装了前端传入的要删除的员工id列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeEmpByIds(List<Integer> ids) {
        // 删除员工基本信息(主表)
        this.removeBatchByIds(ids);
        // 删除员工工作经历信息(从表)
        empExprService.remove(new LambdaQueryWrapper<EmpExpr>().in(EmpExpr::getEmpId, ids));
    }

    /**
     * 根据员工id查询员工详细信息
     * @param id
     * @return 封装了员工详细信息（基本信息和工作经历信息）
     */
    @Override
    public Emp getEmpById(Integer id) {
        Emp emp = this.getById(id);  // 封装员工基本信息
        emp.setExprList(empExprService.list(new LambdaQueryWrapper<EmpExpr>().eq(EmpExpr::getEmpId, id)));  // 封装员工工作经历信息
        return emp;
    }

    /**
     * 修改员工信息
     * @param emp 封装了修改后的员工信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateEmp(Emp emp) {
        // 将前端传入的修改信息(基本信息)同步到数据库员工表（主表）
        emp.setUpdateTime(LocalDateTime.now());
        this.updateById(emp);

        // 根据id删除该员工工作经历表中的全部工作经历信息（从表）
        empExprService.remove(new LambdaQueryWrapper<EmpExpr>().eq(EmpExpr::getEmpId,emp.getId()));

        // 将前端传入的工作经历信息，重新保存到该员工的工作经历表中
        List<EmpExpr> exprList = emp.getExprList();
        if (!CollectionUtils.isEmpty(exprList)){
            // 给每一条工作经历添加对应的员工id，使工作经历表中的emp_id字段不为空，同时保证与员工表的关联
            exprList.forEach(expr -> expr.setEmpId(emp.getId()));
            empExprService.saveBatch(exprList);
        }
    }
}
